home *** CD-ROM | disk | FTP | other *** search
/ MacHack 1996 / MacHack 1996.toast / Hacks / Hacks ’95 / NetFractal™ / Fractal 8 source / FractalMain.c < prev    next >
Encoding:
Text File  |  1995-06-24  |  24.3 KB  |  829 lines  |  [TEXT/MPS ]

  1. /*
  2.     File:        FractalMain.c
  3.  
  4.     Used to build:    “Fractal 8”
  5.     
  6.     Written by:        Jim Cathey            July 1985
  7.                     Eric Traut            November 1994
  8.  
  9.     Description:
  10.         The following code implements a “Fractal Contour” generating
  11.         program. The program was originally written in Microsoft BASIC
  12.         and ported to Aztec C in 1985 by Jim Cathey. It has been a
  13.         widely-distributed public domain application since that time.
  14.  
  15.         The program generates a fractal surface by starting with a 
  16.         triangular surface. It then subdivides the triangle into four
  17.         subtriangles by calculating the midpoint of each edge. The 
  18.         Z coordinate for each of these edges is then randomly incremented
  19.         or decremented. This processes is repeated for each of the
  20.         subtrianges until a jagged fractal surface results.
  21.         
  22.         The original program uses a “cordic” function to approximate
  23.         the calculation of various transcendental functions used in
  24.         three-dimensional graphics transforms.
  25.         
  26.         In 1994, Eric Traut modified the code to run under System 7.5.
  27.         The program was also updated so it could be recompiled under 
  28.         PowerPC. This source is part of a series of modifications made 
  29.         to demonstrate code optimizations under PowerPC.
  30.         
  31.         ------------------------------------------------------------
  32.         
  33.         The following documents the changes made to the program at
  34.         each step of the optimization process and lists the speed
  35.         as measured under a PowerMac 8100/80av (8-bit 13"monitor).
  36.         All stages were compiled using Metrowerks’ CodeWarrier
  37.         4.0 compilers.
  38.         
  39.         
  40.         • STAGE 1:
  41.         0.50 fractals per second
  42.         
  43.         The first step to optimizing for PowerPC is to clean up 
  44.         the source code so it compiles under today’s 68K compilers and
  45.         runs under today’s system software. I also added a feature
  46.         to allow repeated calculation of fractals and a display
  47.         of “fractals per second.” This value will be used to measure
  48.         our progress as we further optimize the program. Note that
  49.         this version runs emulated on a PowerMac.
  50.  
  51.         • STAGE 2:
  52.         1.92 fractals per second
  53.         3.84x emulated speed
  54.         
  55.         This version was created by simply recompiling stage 1 with the
  56.         PowerPC MetroWerks CodeWarrier C compiler. No other changes
  57.         were made.
  58.  
  59.         • STAGE 3:
  60.         2.86 fractals per second
  61.         5.72x emulated speed
  62.         1.49x original native speed
  63.         
  64.         This version was uses an off-screen GWorld for all drawing. This
  65.         naturally uses more memory, but QuickDraw can generally draw to
  66.         an off-screen GWorld faster because of caching characteristics
  67.         (the video buffer is mapped cache-write-through whereas the rest
  68.         of RAM is mapped cache-write-back). The program then uses 
  69.         QuickDraw’s CopyBits routine to copy the image to the screen.
  70.  
  71.         • STAGE 4:
  72.         3.32 fractals per second
  73.         6.64x emulated speed
  74.         1.73x original native speed
  75.         1.16x previous stage
  76.         
  77.         This version replaces QuickDraw’s LineTo with custom offscreen
  78.         line drawing functions. These functions ignore many of the complex
  79.         factors which QuickDraw considers including clipping, color mapping,
  80.         and patterns. Note that bypassing QuickDraw should not be used as
  81.         a general solution for improving performance, but for small specialized
  82.         portions of an app it is OK. Note that this code may run faster under
  83.         a graphics accellerator if QuickDraw were not bypassed.
  84.         
  85.         • STAGE 5:
  86.         6.00 fractals per second
  87.         12.00x emulated speed
  88.         3.13x original native speed
  89.         1.81x previous stage
  90.         
  91.         This version removes a call from the QuickDraw call “GetPixBaseAddr”
  92.         from the DoPlot routine. This routine is emulated on the first
  93.         PowerMacs, so it should be called as infrequently as possible. In
  94.         our case, we do not need to call it in such a tight loop. By moving
  95.         it to the PlotData function, we save considerable time. This version
  96.         also removes the calls to SetCursor to display the busy cursor. We
  97.         are now drawing fast enough that it makes no sense to display it.
  98.         
  99.         • STAGE 6:
  100.         10.00 fractals per second
  101.         20.00x emulated speed
  102.         5.21x original native speed
  103.         1.67x previous stage
  104.         
  105.         This version uses slightly modified routines for caculating and
  106.         plotting fractal surfaces. In general, short variables were changed
  107.         to long. Divisions are avoided wherever possible. All local functions
  108.         are defined as static. Points are drawn with a special function 
  109.         instead of going through the more complex LineTo function. Scaling
  110.         is done with shifts rather than multiplications and divisions. This
  111.         results in a much better performance, but the scaling is not as
  112.         smooth.
  113.         
  114.         • STAGE 7:
  115.         21.27 fractals per second
  116.         42.54x emulated speed
  117.         11.08x original native speed
  118.         2.13x previous stage
  119.         
  120.         This version replaces the previous method’s scaling algorithms.
  121.         Instead of using the cordic algorithm to perform graphical 
  122.         transforms, it uses a simpler linear algebra strategy. This
  123.         results in much faster calculation. To further improve speed,
  124.         this version allocates a second plot point array to record X
  125.         and Y screen coordinates as well as scaled Z coordinates.
  126.         Previous versions recalculated the transforms for each point
  127.         three times -- once when plotting X, Y, and dialognal lines.
  128.         The line drawing routines in this version are also further
  129.         optimized.
  130.         
  131.         • STAGE 8:
  132.         
  133.         This version has no performance improvements over the previous
  134.         stage. Instead, it was written to make a point about performance.
  135.         Once the program speed is sufficient, programmers should try to
  136.         innovate and use the added speed to improve the user interface
  137.         or add some other feature which was previously not possible.
  138.         In this case, I added two new features. The first is a “morphing”
  139.         feature which draws smooth transitions between consequtive fractals.
  140.         This slows down fractal computation but produces a very interesting
  141.         animation effect. The second new feature allows the user to select
  142.         between a wire frame mode (the only option in previous stages) and
  143.         a surface mode. The surface mode draws the complete shaded surface
  144.         by filling in each triangle with a solid color. Thanks to Dan
  145.         Clifford for the tripatch blitting code.
  146.         
  147. */
  148.  
  149. #include "SenderSetup.h"
  150.  
  151. #pragma cplusplus off
  152.  
  153. #include <Types.h>
  154. #include <Memory.h>
  155. #include <Quickdraw.h>
  156. #include <Fonts.h>
  157. #include <Windows.h>
  158. #include <OSUtils.h>
  159. #include <Menus.h>
  160. #include <Events.h>
  161. #include <TextEdit.h>
  162. #include <Dialogs.h>
  163. #include <Desk.h>
  164. #include <Controls.h>
  165. #include <ToolUtils.h>
  166. #include <Resources.h>
  167. #include <Strings.h>
  168. #include <QDOffscreen.h>
  169. #include <Timer.h>
  170. #include <SegLoad.h>
  171.  
  172. #include <stdio.h>
  173. #include <math.h>
  174.  
  175. #include "Fractal.h"
  176.  
  177. /* Functions defined within the file */
  178. void InitApp(void);
  179. void SetUpMenus(void);
  180. void SetUpWindow(void);
  181. void MainEventLoop(void);
  182. void UpdateWindow(WindowPtr theWindow);
  183. void DrawTimeInfo(void);
  184. Boolean DoMenuCommand(long mresult);
  185. void DoSetUpDialog(void);
  186. void SetTerrainButton(DialogPtr ptr, short oncontrl, short offcontrl);
  187. void DoAboutBox(void);
  188. void ReportFatalError(void);
  189. void CleanUpApp(void);
  190. void ClearPointArrays(void);
  191. void CalculateShadeMap(void);
  192.  
  193. /* Global variables */
  194. short             (*gOldPointArray)[kMaxXPoint][kMaxYPoint] = NULL;        /* The array of points to be subdivided */
  195.                                             /* The array of points to be subdivided */
  196. short             (*gNewPointArray)[kMaxXPoint][kMaxYPoint] = NULL;        /* The array of points to be subdivided */
  197.                                             /* The array of points to be subdivided */
  198. ThreePoint         (*gPlotPointArray)[2 * kMaxXPoint][2 * kMaxYPoint] = NULL;        /* The array of points to be subdivided */
  199.                                             /* The array of points to be subdivided */
  200. short            gContourType;                /* Contour type */
  201. short             gContourLevel;                /* Level of detail */
  202. WindowPtr         gMainWindow = NULL;            /* Our one window */
  203. short            gMainWindowHeight;            /* Height of main window */
  204. short            gMainWindowWidth;            /* Width of main window */
  205. Boolean            gContinuousRedraw;            /* Continuously redraw fractal */
  206. long            gMicrosecondCount;            /* Total time spent calculating and drawing */
  207. long            gTotalFractals;                /* Total fractal count */
  208. Boolean            gFractalChanged;            /* Has fractal changed since last update? */
  209. GWorldPtr        gOffscreenGWorld = NULL;    /* GWorld for faster drawing */
  210. PixMapHandle    gOffscreenPixMap = NULL;    /* GWorld’s Pix Map */
  211. float            gMorphAmount;                /* Percent of morph complete */
  212. float            gMorphAmountNeg;            /* 1 - above value */
  213. Boolean            gPlotSurface;                /* Plot surface or wire frame? */
  214. Boolean            gMorphFractal = false;        /* Morph or not morph */
  215. unsigned short    gShadeMap[kShadeMapSize];    /* Mapping of grays */
  216.  
  217.  
  218. /*
  219.     main
  220. */
  221. void main(void) 
  222. {
  223.     InitApp();
  224.     MainEventLoop();
  225.     CleanUpApp();
  226. }
  227.  
  228.  
  229. /*
  230.     CleanUpApp
  231. */  
  232. void CleanUpApp(void)
  233. {
  234.     CleanUpSender();                //•• ES
  235.  
  236.     if (gOffscreenGWorld)
  237.         DisposeGWorld(gOffscreenGWorld);
  238.     
  239.     if (gMainWindow)
  240.         DisposeWindow(gMainWindow);
  241.     
  242.     if (gOldPointArray)
  243.         DisposePtr((Ptr)gOldPointArray);
  244.     
  245.     if (gNewPointArray)
  246.         DisposePtr((Ptr)gNewPointArray);
  247.         
  248.     if (gPlotPointArray)
  249.         DisposePtr((Ptr)gPlotPointArray);
  250.     
  251.     ExitToShell();
  252. }
  253.  
  254.  
  255. /*
  256.     MainEventLoop
  257. */  
  258. void MainEventLoop(void)
  259. {
  260.     EventRecord         curEvent;            /* Event we should respond to */
  261.     WindowPtr             whichWindow;        /* Window which received the click */
  262.     Boolean             userDone;            /* Should we quit the program? */
  263.  
  264.     userDone = false;
  265.  
  266.     /* Get next event, and handle it appropriately, until user quits */
  267.     while (!userDone) {
  268.         
  269.         SetPort(gMainWindow);
  270.         if (WaitNextEvent(everyEvent, &curEvent, 1, NULL)) {
  271.             switch (curEvent.what) {
  272.             case mouseDown:
  273.                 switch (FindWindow(curEvent.where, &whichWindow)) {
  274.                 case inSysWindow:    /* handle the desk accessories */
  275.                     SystemClick(&curEvent, whichWindow);
  276.                     break;
  277.                 case inMenuBar:     /* handle the command */
  278.                     userDone = DoMenuCommand(MenuSelect(curEvent.where));
  279.                     break;
  280.                 case inDrag:         /* No Drag region, treat as content */
  281.                     {
  282.                         Rect        limitRect;
  283.         
  284.                         limitRect = qd.screenBits.bounds;
  285.                         InsetRect(&limitRect, 4, 4);
  286.                         DragWindow(gMainWindow, curEvent.where, &limitRect);
  287.                         break;
  288.                     }
  289.                 case inContent:        /* Activate window */
  290.                     if (whichWindow == gMainWindow)
  291.                         if (whichWindow != FrontWindow())
  292.                             SelectWindow(whichWindow);
  293.                     break;
  294.                 case inGrow:         /* No Grow Region */
  295.                 case inGoAway:         /* We don’t have a GoAway region */
  296.                             break;
  297.                 }
  298.                 break;
  299.  
  300.             case keyDown: 
  301.             case autoKey:            /* If command key, pass the char to MenuKey */
  302.                 if (curEvent.modifiers & cmdKey) 
  303.                     userDone = DoMenuCommand(MenuKey((char)(curEvent.message & charCodeMask)));
  304.                 break;
  305.             case updateEvt:            /* If it’s for our window, update it */
  306.                 if ((WindowPtr)curEvent.message == gMainWindow)
  307.                     UpdateWindow(gMainWindow);
  308.                     break;
  309.             case activateEvt:         /* If for our window, set port as necessary */
  310.                 if ((WindowPtr)curEvent.message == gMainWindow) {
  311.                     if (curEvent.modifiers & 1) {
  312.                                     /* odd means an activate event */
  313.                         SetPort(gMainWindow);
  314.                         DisableItem(GetMenu(kEditMenuID), 0);
  315.                         EnableItem(GetMenu(kFileMenuID), 0);
  316.                         EnableItem(GetMenu(kOptionsMenuID), 0);
  317.                         DrawMenuBar();
  318.                     }
  319.                     else {
  320.                         EnableItem(GetMenu(kEditMenuID), 0);
  321.                         DisableItem(GetMenu(kFileMenuID), 0);
  322.                         DisableItem(GetMenu(kOptionsMenuID), 0);
  323.                         DrawMenuBar();
  324.                     }
  325.                 }
  326.                 break;
  327.             }
  328.         }
  329.         else {
  330.             if (gContinuousRedraw) {
  331.                 if (gMorphFractal) {
  332.                     if (gMorphAmount >= 1.0) {
  333.                         void*        tempPtr;
  334.     
  335.                         tempPtr = gNewPointArray;
  336.                         gNewPointArray = gOldPointArray;
  337.                         gOldPointArray = tempPtr;
  338.                         
  339.                         CalcSurface(gContourLevel);
  340.                     }
  341.                     else {
  342.                         /* Morphing is too fast for levels under 7, so we will
  343.                             hold it back by creating more intermediate frames */
  344.                         if (gContourLevel == kMaxLevel)
  345.                             gMorphAmount += 0.2;
  346.                         else
  347.                             gMorphAmount += 0.1;
  348.                         gFractalChanged = true;
  349.                         InvalRect(&gMainWindow->portRect);
  350.                         UpdateWindow(gMainWindow);
  351.                     }
  352.                 }
  353.                 else {
  354.                     gMorphAmount += 1.0;
  355.                     CalcSurface(gContourLevel);
  356.                     InvalRect(&gMainWindow->portRect);
  357.                     UpdateWindow(gMainWindow);
  358.                 }
  359.             }
  360.         }    
  361.     }
  362. }
  363.  
  364.  
  365. /*
  366.     DoMenuCommand
  367.  
  368.    This function responds to the menu command returned by MenuSelect.
  369.    If it was Quit, we return true, else false.  Since the menu was
  370.    highlighted by MenuSelect, we must finish by unhighlighting it
  371.    to indicate we're done.
  372. */
  373. Boolean DoMenuCommand(long menuResult)
  374. {
  375.     short            menuID;                /* menu ID of selected menu */
  376.     short            itemNumber;            /* item number of selected menu item */
  377.     Boolean            quitSelected;        /* was menu item Quit? */
  378.  
  379.     quitSelected = false;                /* Assume Quit not selected */
  380.     menuID = HiWord(menuResult);        /* Get the menu selected */
  381.     itemNumber = LoWord(menuResult);    /* ... and the item of that menu */
  382.  
  383.     switch (menuID) {
  384.     case kAppleMenuID: 
  385.         if (itemNumber == kAboutBoxItem)    /* Tell about FracCont */
  386.             DoAboutBox();
  387.         else {                              /* Run a desk accessory */
  388.             Str255            menuName;
  389.             GrafPtr         savedPort;
  390.                 
  391.             GetPort(&savedPort);            /* Preserve port */
  392.             GetMenuItemText(GetMenu(kAppleMenuID), itemNumber, menuName);
  393.             (void) OpenDeskAcc(menuName);    /* Run the desk accessory */
  394.             SetPort(savedPort);                /* Restore port */
  395.         }
  396.         break;
  397.  
  398.         case kFileMenuID: 
  399.             switch (itemNumber) {
  400.                 case kNewFractalItem:       /* New Surface */
  401.                         CalcSurface(gContourLevel);
  402.                         InvalRect(&gMainWindow -> portRect);
  403.                         break;
  404.                 case kQuitItem: 
  405.                     quitSelected = true;    /* Quit */
  406.                     break;
  407.             }
  408.             break;
  409.  
  410.         case kOptionsMenuID:
  411.             switch (itemNumber) {
  412.             case kSetupItem:
  413.                 DoSetUpDialog();
  414.                 break;
  415.             case kContinuousItem:
  416.                 SetItemMark(GetMenu(kOptionsMenuID), kContinuousItem, gContinuousRedraw ?
  417.                         noMark : checkMark);
  418.                 gContinuousRedraw = !gContinuousRedraw;
  419.                 if (gContinuousRedraw)
  420.                     gMorphAmount = 0.0;
  421.                 else
  422.                     gMorphAmount = 1.0;
  423.                 break;
  424.             case kMorphItem:
  425.                 SetItemMark(GetMenu(kOptionsMenuID), kMorphItem, gMorphFractal ?
  426.                         noMark : checkMark);
  427.                 gMorphFractal = !gMorphFractal;
  428.                 gFractalChanged = true;
  429.                 InvalRect(&gMainWindow->portRect);
  430.                 break;
  431.             case kDrawSurfaceItem:
  432.                 SetItemMark(GetMenu(kOptionsMenuID), kDrawSurfaceItem, gPlotSurface ?
  433.                         noMark : checkMark);
  434.                 gPlotSurface = !gPlotSurface;
  435.                 gFractalChanged = true;
  436.                 InvalRect(&gMainWindow->portRect);
  437.                 break;
  438.             case kDrawSurfaceItem+2:            //•• ES: yes this is ugly
  439.                 SpawnSender();
  440.                 break;    
  441.             }
  442.         break;
  443.     }
  444.  
  445.     HiliteMenu(0);                            /* Turn off hilighting on the menu just used */
  446.     return quitSelected;
  447. }
  448.  
  449.  
  450. /*
  451.     SetTerrainButton
  452.     
  453.     This function is called by the DoSetUpDialog function to change
  454.     the current terrain button.
  455. */
  456. void SetTerrainButton(DialogPtr theDialog, short newControlID, short oldControlID)
  457. {
  458.     short                itemType;
  459.     ControlHandle        theControl;
  460.     Rect                itemBox;
  461.  
  462.     GetDialogItem(theDialog, oldControlID, &itemType, (Handle*)&theControl, &itemBox);
  463.     SetControlValue(theControl, 0);     /* Turn off old default button */
  464.     GetDialogItem(theDialog, newControlID, &itemType, (Handle*)&theControl, &itemBox);
  465.     SetControlValue(theControl, 1);     /* Turn on default button */
  466. }
  467.         
  468.  
  469. /*
  470.     DoSetUpDialog
  471.     
  472.     This function displays the set-up dialog and handles user actions
  473.     while the dialog is up.
  474. */
  475. void DoSetUpDialog(void)
  476. {
  477.     DialogPtr         theDialog;                /* Pointer to dialog */
  478.     long             newType;                /* Selected contour type */
  479.     long             newLevel;                /* Selected level of detail */
  480.     Boolean         doAnother;                /* True if we need to recalc */
  481.     short             itemHit = 0;            /* Item number of selected item */
  482.     Str255             itemText;                /* Used for converting string to num */
  483.     short             itemType;                /* Dummy variable needed for GetDialogItem */
  484.     Handle            itemHandle;                /* Item handle returned by GetDialogItem */
  485.     Rect            itemBox;                /* Item bound box returned by GetDialogItem */
  486.         
  487.     newType = gContourType;                    /* Initialize local vars to current values */
  488.     newLevel = gContourLevel;
  489.     doAnother = false;
  490.  
  491.     /* Display the dialog and set initial values if controls */
  492.     theDialog = GetNewDialog(kSetUpDialogID, NULL, (WindowPtr) -1);
  493.     SetPort(theDialog);
  494.     
  495.     SetTerrainButton(theDialog, gContourType, gContourType);
  496.     NumToString(newLevel, itemText);
  497.     GetDialogItem(theDialog, kSetUpLevelID, &itemType, &itemHandle, &itemBox);
  498.     SetDialogItemText(itemHandle, itemText);
  499.     SelectDialogItemText(theDialog, kSetUpLevelID, 0, 32767);    /* Hilite entire text field. */
  500.     
  501.     while (itemHit != kSetUpOKButtonID && itemHit != kSetUpCancelButtonID) {
  502.         ModalDialog(NULL, &itemHit);
  503.         GetDialogItem(theDialog, kSetUpLevelID, &itemType, &itemHandle, &itemBox);
  504.         GetDialogItemText(itemHandle, itemText);                /* Get Level field */
  505.         StringToNum(itemText, &newLevel);
  506.         
  507.         /* Check for legal contour level */
  508.         if (newLevel < 1 || newLevel > kMaxLevel) {
  509.             SysBeep(0);
  510.             newLevel = gContourLevel;
  511.             NumToString(newLevel, itemText);
  512.             GetDialogItem(theDialog, kSetUpLevelID, &itemType, &itemHandle, &itemBox);
  513.             SetDialogItemText(itemHandle, itemText);
  514.             SelectDialogItemText(theDialog, kSetUpLevelID, 0, 100);  /* Hilite text field. */
  515.         }
  516.  
  517.         if (itemHit == kSetUpMtnButtonID || 
  518.                 itemHit == kSetUpHillsButtonID ||
  519.                 itemHit == kSetUpWaterButtonID) {
  520.             SetTerrainButton(theDialog, itemHit, newType);
  521.             newType = itemHit;
  522.         }
  523.  
  524.         if (itemHit == kSetUpOKButtonID && ((newLevel > 0) & (newLevel <= kMaxLevel))) {
  525.             if (gContourType != newType || gContourLevel != newLevel) {
  526.                 ClearPointArrays();
  527.                 doAnother = true;
  528.                 SetPort(gMainWindow);
  529.                 InvalRect(&gMainWindow->portRect);
  530.                 gContourLevel = newLevel;
  531.                 gContourType = newType;
  532.             }
  533.         }
  534.     }
  535.     
  536.     DisposeDialog(theDialog);        /* Release storage and remove dialog from screen */
  537.  
  538.     if (doAnother) {
  539.         gMicrosecondCount = 0;
  540.         gTotalFractals = 0;
  541.         CalcSurface(newLevel);
  542.         InvalRect(&gMainWindow->portRect);
  543.         UpdateWindow(gMainWindow);
  544.     }
  545. }
  546.  
  547.  
  548. /*
  549.     DoAboutBox
  550.     
  551.     This function displays the about box dialog.
  552. */
  553. void DoAboutBox(void)
  554. {
  555.     short            itemHit;            
  556.     DialogPtr        theDialog;
  557.  
  558.     theDialog = GetNewDialog(kAboutBox1DialogID, NULL, (WindowPtr) -1);
  559.     if (theDialog == NULL)
  560.         ReportFatalError();
  561.         
  562.     SetPort(theDialog);
  563.     
  564.     ModalDialog(NULL, &itemHit);
  565.     DisposeDialog(theDialog);
  566.  
  567.     if (itemHit == kAboutBoxMoreButtonID) {
  568.         theDialog = GetNewDialog(kAboutBox2DialogID, NULL, (WindowPtr) -1);
  569.         ModalDialog(NULL, &itemHit);
  570.         DisposeDialog(theDialog);
  571.     }
  572. }
  573.  
  574.  
  575. /*
  576.     SetUpMenus
  577.     
  578.     This function initilizes the menu bar and the applications menus.
  579. */
  580. void SetUpMenus(void)
  581.  {
  582.     SetMenuBar(GetNewMBar(kMenuBarID));
  583.     AppendResMenu(GetMenu(kAppleMenuID), 'DRVR');
  584.     DrawMenuBar();
  585. }
  586.  
  587.  
  588. /*
  589.     SetUpWindow
  590.     
  591.     This function initializes the main window and its associated
  592.     global variables. It also sets up the offscreen GWorld for
  593.     our drawing and screen updates.
  594. */
  595. void SetUpWindow(void)
  596. {
  597.     GDHandle            mainDeviceHandle;
  598.     short                maxWindowHeight, maxWindowWidth;
  599.     Rect                offscreenRect;
  600.  
  601.     gMainWindow = GetNewCWindow(kMainWindowID, NULL, (WindowPtr)-1);
  602.      mainDeviceHandle = GetMainDevice();
  603.      
  604.     maxWindowHeight = (*mainDeviceHandle)->gdRect.bottom - (*mainDeviceHandle)->gdRect.top - 
  605.              (2 * kScreenBoundaryBits) - GetMBarHeight() - kWindowTitleHeight;
  606.      maxWindowWidth = (*mainDeviceHandle)->gdRect.right - (*mainDeviceHandle)->gdRect.left - 
  607.              (2 * kScreenBoundaryBits);
  608.      
  609.      if (maxWindowHeight >= kNewScreenY && maxWindowWidth >= kNewScreenX) {
  610.          gMainWindowHeight = kNewScreenY;
  611.          gMainWindowWidth = kNewScreenX;
  612.      }
  613.      else {
  614.          gMainWindowHeight = maxWindowHeight;
  615.          gMainWindowWidth = maxWindowWidth;
  616.      }
  617.      
  618.      SizeWindow(gMainWindow, gMainWindowWidth, gMainWindowHeight, false);
  619.      MoveWindow(gMainWindow,
  620.              (*mainDeviceHandle)->gdRect.left + kScreenBoundaryBits,
  621.              (*mainDeviceHandle)->gdRect.top + kScreenBoundaryBits + GetMBarHeight() + kWindowTitleHeight,
  622.              false);
  623.  
  624.     /* Now, allocate the offscreen GWorld */
  625.      SetRect(&offscreenRect, 0, 0, kNewScreenX, kNewScreenY);
  626.      (void) NewGWorld(&gOffscreenGWorld, 8, &offscreenRect, NULL, mainDeviceHandle, 0);
  627.     
  628.     if (gOffscreenGWorld == NULL)
  629.         ReportFatalError();
  630.  
  631.      if (gOffscreenGWorld)
  632.         gOffscreenPixMap = GetGWorldPixMap(gOffscreenGWorld);
  633.  
  634.      ShowWindow(gMainWindow);
  635. }
  636.  
  637.  
  638. /*
  639.     CalculateShadeMap
  640.     
  641.     This function calculates the pixel values for a range of
  642.     grays. It does this by calculating the amount of light
  643.     reflected from a given triangular patch if we assume
  644.     a white light positioned directly above.
  645. */
  646. void CalculateShadeMap()
  647. {
  648.     long    count;
  649.     
  650.     for (count = 0; count < kShadeMapSize; count++) {
  651.     
  652.         double     vLength;
  653.         
  654.         vLength = sqrt(kVectorBaseSize * kVectorBaseSize + (count * count));
  655.     
  656.         gShadeMap[count] = (kTotalGrays - 1) - ((count * kTotalGrays) / vLength);
  657.         
  658.         if (gShadeMap[count] < 2)
  659.             gShadeMap[count] = 2;
  660.         
  661.         if (gShadeMap[count] > kTotalGrays - 1)
  662.             gShadeMap[count] = kTotalGrays - 1;
  663.     }
  664. }
  665.  
  666.  
  667. /*
  668.     InitApp
  669.     
  670.     This function initializes the program the Mac toolbox.
  671. */
  672. void InitApp(void)
  673. {
  674.     InitGraf(&qd.thePort);
  675.     InitFonts();
  676.     InitWindows();
  677.     InitMenus();
  678.     TEInit();
  679.     InitDialogs(NULL);
  680.     InitCursor();
  681.     
  682.     InitSender();                //•• ES
  683.     
  684.      gContinuousRedraw = false;
  685.      gPlotSurface = false;
  686.     gMicrosecondCount = 0;                    /* Initialize performance counters */
  687.     gTotalFractals = 0;
  688.  
  689.     SetUpMenus();                            /* Set up our menus */
  690.     gContourType = kDefaultStyle;            /* Default style */
  691.     gContourLevel = kDefaultLevel;            /* Default Level */
  692.  
  693.     /* Allocate the data arrays */
  694.     gOldPointArray = (void*)NewPtr((long) kMaxXPoint*kMaxYPoint*(sizeof(short)));
  695.     gNewPointArray = (void*)NewPtr((long) kMaxXPoint*kMaxYPoint*(sizeof(short)));
  696.     gPlotPointArray = (void*)NewPtr((long) (2 * kMaxXPoint)*(kMaxYPoint * 2)*(sizeof(ThreePoint)));
  697.  
  698.     /* Make sure we got some memory */
  699.     if (gOldPointArray == NULL || gNewPointArray == NULL || gPlotPointArray == NULL)
  700.         ReportFatalError();
  701.  
  702.     ClearPointArrays();
  703.  
  704.      SetUpWindow();                            /* Create new window */
  705.     CalculateShadeMap();
  706.  
  707.     CalcSurface(gContourLevel);             /* Do at least one first. */
  708. }
  709.  
  710.  
  711. /*
  712.     UpdateWindow
  713.  
  714.     This is our response to receipt of an update event for 
  715.     gMainWindow. It redraws our main window, respecting the
  716.     current clip region, etc.
  717. */
  718. void UpdateWindow(WindowPtr theWindow)
  719. {
  720.     GrafPtr                savedPort;            /* Used to temporarily save cur port */
  721.     Rect                windowRect;            /* Rect of entire window */
  722.     UnsignedWide        startTime, endTime;
  723.  
  724.     GetPort(&savedPort);            /* Save current port */
  725.     SetPort(theWindow);                /* Work in the specified window */
  726.     BeginUpdate(theWindow);
  727.     
  728.     Microseconds(&startTime);
  729.  
  730.     LockPixels(gOffscreenPixMap);
  731.  
  732.     if (gFractalChanged) {
  733.         GDHandle            curGDevice;
  734.     
  735.         curGDevice = GetGDevice();
  736.         SetGWorld((CGrafPtr)gOffscreenGWorld, NULL);
  737.         SetRect(&windowRect, 0, 0, kNewScreenX, kNewScreenY);
  738.         EraseRect(&windowRect);
  739.         PlotData(gPlotSurface);                        /* Redraw contents of window */
  740.         SetGDevice(curGDevice);
  741.     }
  742.  
  743.     SetPort(gMainWindow);
  744.     CopyBits((BitMap*)*gOffscreenGWorld->portPixMap, 
  745.             &gMainWindow->portBits,
  746.             &(*gOffscreenGWorld->portPixMap)->bounds,
  747.             &gMainWindow->portRect,
  748.             srcCopy,
  749.             NULL);
  750.     BlastData();                                //•• ES
  751.     UnlockPixels(gOffscreenPixMap);
  752.  
  753.     /* If this is an update in response to a recomputation, we will count it
  754.         as a part of the total time for the current fractal. If the update is
  755.         in response to something else (e.g. the window coming to the front),
  756.         we won’t time it. */
  757.     if (gFractalChanged) {
  758.         gFractalChanged = false;
  759.         Microseconds(&endTime);
  760.         if (startTime.hi == endTime.hi)
  761.             gMicrosecondCount += endTime.lo - startTime.lo;
  762.         else
  763.             gMicrosecondCount += startTime.lo - endTime.lo;
  764.     }
  765.     
  766.     DrawTimeInfo();                    /* Draw performance statistics */
  767.     
  768.     SetPort(savedPort);                /* Restore previous port */
  769.     EndUpdate(theWindow);
  770. }
  771.  
  772.  
  773. /*
  774.     DrawTimeInfo
  775. */
  776. void DrawTimeInfo(void)
  777. {
  778.     Str255        tempString;            /* Used for creating statistics string */
  779.     Rect        tempRect;            /* Rect to erase */
  780.  
  781.     TextFont(geneva);
  782.     TextSize(12);
  783.     MoveTo(20, gMainWindowHeight - 5);
  784.     DrawString("\pFractals per second: ");
  785.  
  786.     if (gMicrosecondCount != 0) {        /* Make sure we don’t divide by zero */
  787.         SetRect(&tempRect, 150, gMainWindowHeight - 20, 200, gMainWindowHeight);
  788.         EraseRect(&tempRect);
  789.         sprintf((char*)tempString, "%3.3f", 
  790.                 (double)gTotalFractals * 1000000 / (double)gMicrosecondCount);
  791.         c2pstr((char*)tempString);
  792.         DrawString(tempString);
  793.     }
  794. }
  795.  
  796.  
  797. /*
  798.     ClearPointArrays
  799. */
  800. void ClearPointArrays(void)
  801. {
  802.     long        curX, curY;
  803.     
  804.     for (curX = 0; curX < kMaxXPoint; curX++) {
  805.         for (curY = 0; curY < kMaxYPoint; curY++) {
  806.             (*gOldPointArray)[curX][curY] = 0;
  807.             (*gNewPointArray)[curX][curY] = 0;
  808.         }
  809.     }
  810. }
  811.  
  812.  
  813. /*
  814.     ReportFatalError
  815.     
  816.     This function displays a fatal error alert and terminates the program.
  817. */
  818. void ReportFatalError(void)
  819. {
  820.     (void) Alert(kFatalErrorAlertID, NULL);
  821.     CleanUpApp();
  822. }
  823.  
  824.  
  825.  
  826.  
  827.  
  828.  
  829.